package main

import (
	"fmt"
)

/*
Go 中没有关键字显式声明某个类型实现了某个接口。
只要一个类型实现了接口要求的所有方法，该类型就自动被认为实现了该接口。
*/
type Shape interface {
	getArea() float64
	getPerimeter() float64
}

// 定义一个结构体，这个结构体象征意义上是“圆形”
type Circle struct {
	//首字母大写的成员变量，对其他包可见，public
	//在json序列化反序列化过程中，public成员变量能够转换
	Radius float64 `json:"radius"`
	Age    int32   `json:"-"` //tag标记甚至可以手动忽略指定public成员变量不让它转换
	//首字母小写，则为private
	//private成员变量无法json转换，但可以通过tag标记，实现转换
	name string `json:"name"` //这就是tag标记
}

// 这个函数就是属于结构体圆形的其中一个方法；
func (c *Circle) getArea() float64 {
	var result float64
	result = (*c).Radius * (*c).Radius * 3.14
	return result
}

// 两个抽象函数都被实现，代表结构体Circle完全实现了接口Shape
func (c *Circle) getPerimeter() float64 {
	var result float64
	result = 2 * 3.14 * (*c).Radius
	return result
}

// 参数可以分着写类型；返回值只写类型
func isMax1(num1 int32, num2 int32) int32 {
	var result int32

	if num1 > num2 {
		result = num1
	} else {
		result = num2
	}

	return result
}

// 参数类型一致可以合并写类型
func isMax2(num1, num2 int32) int32 {
	var result int32

	if num1 > num2 {
		result = num1
	} else {
		result = num2
	}

	return result
}

// 返回值可以多个，用括号括起来;哪怕返回值同类型，在返回多个返回值情况下也必须写多个
func printtest(str string) (string, string, string) {
	var strBegin string = "HELLO "
	var strEnd string = " WORLD"
	return strBegin, str, strEnd
}

// 引用传递，使用指针
func swapInt32Pointer(num1 *int32, num2 *int32) (int32, int32) {
	var result1, result2, temp int32

	temp = *num1
	*num1 = *num2
	*num2 = temp

	result1 = *num1
	result2 = *num2

	return result1, result2
}

// 闭包，也就是可以批量生成匿名函数的函数
func getSequence() func() int32 {
	//getSequence是闭包
	var i int32 = 0
	//return返回的是匿名函数
	return func() int32 {
		i++
		return i
	}
}

func getOperation() func(x int32, y int32) int32 {
	return func(x int32, y int32) int32 {
		var result int32 = -1
		result = x + y
		return result
	}
}

func main() {
	var a int32 = 32
	var b int32 = 64
	var result int32
	result = isMax1(a, b)
	fmt.Printf("isMax1输出结果是 %d \n", result)
	var stra, strb, strc string
	stra, strb, strc = printtest("xueqiu")
	fmt.Println(stra + strb + strc)

	var c, d int32
	c, d = swapInt32Pointer(&a, &b)
	fmt.Printf("c = %d, d = %d, a = %d, b = %d \n", c, d, a, b)
	//fun1就是匿名函数的对象
	var fun1 (func() int32) = getSequence()
	c = fun1()
	fmt.Printf("c = %d \n", c)
	c = fun1()
	fmt.Printf("c = %d \n", c)
	c = fun1()
	fmt.Printf("c = %d \n", c)
	c = fun1()
	fmt.Printf("c = %d \n", c)
	c = fun1()
	fmt.Printf("c = %d \n", c)
	//fun1的i与fun2的i不一样，独立的
	var fun2 (func() int32) = getSequence()
	d = fun2()
	fmt.Printf("d = %d \n", d)
	fmt.Printf("c = %d \n", c)

	var operationParent (func(operation func(x1 int32, y1 int32) int32, x int32, y int32) int32) = func(operation func(x1 int32, y1 int32) int32, x int32, y int32) int32 {
		var result int32 = -1
		result = operation(x, y)
		return result
	}
	var add (func(x int32, y int32) int32) = getOperation()
	var e int32 = operationParent(add, 10, 20)
	fmt.Printf("e = %d \n", e)

	//声明方式1
	var c1 *Circle = &Circle{Radius: 10.0, name: "可以忽略部分成员变量"}
	//声明方式2
	var c2 *Circle = &Circle{20.0, 20, "必须把所有成员变量全部写上"}
	//声明方式3
	var c3 Circle = Circle{Radius: 10.0, name: "可以忽略部分成员变量"}
	//声明方式4
	var c4 Circle = Circle{20.0, 20, "必须把所有成员变量全部写上"}
	//声明方式5
	//var c5 := Circle{}
	//var c6 := &Circle{}
	//声明方式6,匿名结构体
	/*
		person := struct {
		    Name string
		    Age  int
		}{
		    Name: "Bob",
		    Age:  30,
		}
	*/
	//声明方式7,数组
	/*
		circles := [2]Circle{
		    {Radius: 5.0},
		    {Radius: 10.0},
		}
	*/
	//声明方式8,切片
	/* circleSlice := []Circle{
	    {Radius: 2.5},
	    {Radius: 7.5},
	} */
	//声明方式9,指针数组
	/* 	circlePointers := [2]*Circle{
	    &Circle{Radius: 5.0},
	    &Circle{Radius: 10.0},
	} */
	//声明方式10,指针切片
	/* circlePointerSlice := []*Circle{
	    &Circle{Radius: 2.5},
	    &Circle{Radius: 7.5},
	} */
	//声明方式11，new()函数
	/*
		使用 new 初始化结构体
		c 是 *Circle 类型，字段被初始化为零值
		c := new(Circle)
		new 的作用：分配内存并返回指向零值初始化的结构体的指针。
		使用 new 创建的结构体，所有字段都会按照 Go 的零值规则进行初始化。例如：
		    数字类型（如 int, float64）为 0。
		    字符串为 ""。
		    布尔值为 false。
		    指针为 nil。

	*/

	//使用方式1，自动解引用（仅限1层指针）
	c1.Radius = 20.0
	fmt.Printf("c1.radius = %f, c1.area = %f \n", c1.Radius, c1.getArea())
	//使用方式2，传统加星号
	(*c2).Radius = 30.0
	fmt.Printf("c2.radius = %f, c2.area = %f \n", (*c2).Radius, (*c2).getArea())
	//使用方式3，对象的话直接使用成员变量和方法
	c3.Radius = 40.0
	fmt.Printf("c3.radius = %f, c3.area = %f \n", c3.Radius, c3.getArea())
	fmt.Printf("c4.radius = %f, c4.area = %f \n", c4.Radius, c4.getArea())
}
